﻿using Import.API;
using Import.Lib;
using Import.Lib.IRS;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace IndexingHandler
{
    public class QueueHandler
    {
        public LogHandler logHandler;

        public DBHandler dbHandler; // Подключение к БД
        public string configFile; // Путь до конфигурационного файла индексатора
        public bool CommitSolr; // Коммитить или нет в Solr

        public ImportAPI iApi; // Индексатор
        public StartParams sParams; // Параметры запуска индексатора
        public int importKey; // Идектификатор очереди индексации

        public QueueHandler()
        {
        }

        // Функция проверки корректности подключения и возможности работы обработчика
        private bool connectionIsOk()
        {
            if (dbHandler == null)
            {
                logHandler.WriteLogStr("Wrong run parameters: dbHandler is null");
                return false;
            }
            bool connResult = dbHandler.Connect();
            if (!connResult)
            {
                logHandler.WriteLogStr(dbHandler.connectException.Message);
                return false;
            }
            return true;
        }

        // Процедура отметки начала индексации
        private void updateStartDate(List<QueueStructure> IndexingQueue)
        {
            DateTime StartIndexingDate = DateTime.Now;
            foreach (QueueStructure queueStructure in IndexingQueue)
            {
                try
                {
                    string queryText = "UPDATE [" + dbHandler.Schema + "].[CDW_INDEX_QUEUE] SET START_INDEXING_DATE = @Start_Indexing_Date, STATUS_ID = @Status_Id WHERE QUEUE_ID = @Queue_Id";
                    using (SqlCommand dbCommand = new SqlCommand(queryText, dbHandler.connection))
                    {
                        dbCommand.Parameters.Add("Start_Indexing_Date", SqlDbType.DateTime).Value = StartIndexingDate;
                        dbCommand.Parameters.Add("Queue_Id", SqlDbType.NChar).Value = queueStructure.QUEUE_ID;
                        dbCommand.Parameters.Add("Status_Id", SqlDbType.Int).Value = QueueStatuses.Processing;
                        dbCommand.ExecuteNonQuery();
                    }
                }
                catch (Exception e)
                {
                    logHandler.WriteLogStr("updateStartDate: " + e.Message);
                }
            }
        }

        // Процедура отметки окончания индексации
        public void updateProcessingResult(List<QueueStructure> IndexingQueue)
        {
            //DateTime EndIndexingDate = DateTime.Now;
            foreach (QueueStructure queueStructure in IndexingQueue)
            {
                try
                {
                    string queryText = "UPDATE [" + dbHandler.Schema + "].[CDW_INDEX_QUEUE] SET START_INDEXING_DATE = @Start_Indexing_Date, END_INDEXING_DATE = @End_Indexing_Date, STATUS_ID = @Status_Id, LOAD_ERROR_INFO = @Load_Error_Info WHERE QUEUE_ID = @Queue_Id";
                    using (SqlCommand dbCommand = new SqlCommand(queryText, dbHandler.connection))
                    {
                        dbCommand.Parameters.Add("Start_Indexing_Date", SqlDbType.DateTime).Value = queueStructure.START_INDEXING_DATE;
                        dbCommand.Parameters.Add("End_Indexing_Date", SqlDbType.DateTime).Value = queueStructure.END_INDEXING_DATE;
                        dbCommand.Parameters.Add("Status_Id", SqlDbType.Int).Value = queueStructure.STATUS_ID;
                        dbCommand.Parameters.Add("Load_Error_Info", SqlDbType.NChar).Value = queueStructure.LOAD_ERROR_INFO;
                        dbCommand.Parameters.Add("Queue_Id", SqlDbType.NChar).Value = queueStructure.QUEUE_ID;
                        dbCommand.ExecuteNonQuery();
                        // Залогируем результат индексации
                        logHandler.WriteLogStr("\t Indexing result: [QUEUE_ID = " + queueStructure.QUEUE_ID + "; KEY = " + queueStructure.KEY + "; OBJECT_ID = " + queueStructure.OBJECT_ID + "; STATUS_ID = " + queueStructure.STATUS_ID + "; LOAD_ERROR_INFO = " + queueStructure.LOAD_ERROR_INFO + "]");
                    }
                }
                catch (Exception e)
                {
                    logHandler.WriteLogStr("updateProcessingResult: " + e.Message);
                }
            }
        }
        
        // Процедура инициализации нидексатора и выполнение процедуры индексации
        private void StartIndexer(QueueStructure queueStructure)
        {
            ImportAPI iApi;
            if (configFile == "") iApi = new ImportAPI();
            else iApi = new ImportAPI(configFile);
            StartParams sParams = new StartParams();
            sParams.Packages.Clear();
            sParams.AdvancedParams.InputSQLQuery = "Select " + queueStructure.KEY + " as portalKey";
            sParams.AdvancedParams.Commit = CommitSolr;
            sParams.ImportType = ImportStartParams.ImportTypes.Sources;
            importKey = iApi.StartImport(sParams);
        }

        // Функция получения запроса для индексации нескольких каталогов
        private string getInputQuery(List<QueueStructure> IndexingQueue)
        {
            string queryStr = "";
            int i = 0;
            foreach (QueueStructure queueRecord in IndexingQueue)
            {
                i ++;
                if (i == IndexingQueue.Count) queryStr = queryStr + "Select " + queueRecord.PORTALKEY + " as portalKey";
                else queryStr = queryStr + "Select " + queueRecord.KEY + " as portalKey UNION ALL ";
            }
            return queryStr;
        }
        
        // Процедура обработки списка каталогов
        public void IndexCatalogList(List<QueueStructure> IndexingQueue)
        {
            try
            {
                if (configFile == "") iApi = new ImportAPI();
                else iApi = new ImportAPI(configFile);
                sParams = new StartParams();
                sParams.Packages.Clear();
                sParams.AdvancedParams.InputSQLQuery = getInputQuery(IndexingQueue);
                sParams.ImportType = ImportStartParams.ImportTypes.Sources;
                importKey = iApi.StartImport(sParams);
            }
            catch (Exception e)
            {
                logHandler.WriteLogStr("IndexCatalogList: " + e.Message);
            }
            updateStartDate(IndexingQueue);
        }
        
        // Процедура просмотра очереди и запуска индексатора (обрабатываем всю очередь)
        public void HandleQueue()
        {
            List<QueueStructure> IndexingQueue = GetIndexingQueue(true);
            logHandler.WriteLogStr("HandleQueue - Start");
            IndexCatalogList(IndexingQueue);
            logHandler.WriteLogStr("HandleQueue - Stop");
        }

        // Функция получения очереди на индексацию
        public List<QueueStructure> GetIndexingQueue(bool NeedToLog)
        {
            List<QueueStructure> IndexingQueue = new List<QueueStructure>();
            // Проверим корректность подключения
            if (!connectionIsOk()) return IndexingQueue;
            // Получим очередь
            if (NeedToLog) logHandler.WriteLogStr("GetIndexingQueue - Start");
            int queueLength = 0;
            try
            {
                string queryText = "SELECT * FROM [" + dbHandler.Schema + "].[CDW_INDEX_QUEUE] WHERE STATUS_ID in (1, 4)"; // Новые и ошибочные
                using (SqlCommand dbCommand = new SqlCommand(queryText, dbHandler.connection))
                {
                    //dbCommand.Parameters.Add("Status_id", SqlDbType.Int).Value = Status_id;
                    using (SqlDataReader reader = dbCommand.ExecuteReader())
                    {
                        if (reader != null)
                        {
                            while (reader.Read())
                            {
                                IndexingQueue.Add(new QueueStructure
                                {
                                    QUEUE_ID = reader["QUEUE_ID"],
                                    KEY = reader["KEY"],
                                    OBJECT_ID = reader["OBJECT_ID"],
                                    INSERT_DATE = reader["INSERT_DATE"],
                                    START_INDEXING_DATE = reader["START_INDEXING_DATE"],
                                    END_INDEXING_DATE = reader["END_INDEXING_DATE"],
                                    STATUS_ID = reader["STATUS_ID"],
                                    LOAD_ERROR_INFO = reader["LOAD_ERROR_INFO"],
                                    PORTALKEY = reader["PORTALKEY"],
                                }
                                                    );
                                queueLength++;
                                // Запишем в лог считанную запись
                                if (NeedToLog)
                                {
                                    logHandler.WriteLogStr("\t Queue record: [QUEUE_ID = " + reader["QUEUE_ID"] + "; KEY = " + reader["KEY"] + "; OBJECT_ID = " + reader["OBJECT_ID"] + "]");
                                }
                            }
                        }
                    }

                }
            }
            catch (Exception e)
            {
                logHandler.WriteLogStr("GetIndexingQueue: " + e.Message);
            }
            if (NeedToLog) logHandler.WriteLogStr("GetIndexingQueue - Stop (" + queueLength + " records were read)");
            return IndexingQueue;
        }

        // Функция, возвращающая статус очереди индексации
        public IndexingStatus GetQueueStatus()
        {
            IndexingStatus indexingStatus = new IndexingStatus();
            try
            {
                var statusResult = new ImportRecord();
                if (iApi == null)
                {
                    indexingStatus.Status = QueueStatuses.Processed;
                    return indexingStatus;
                }
                // Получим общий статус индексации
                statusResult = iApi.GetStatus(importKey);
                indexingStatus.TotalCount = statusResult.TotalCount;
                indexingStatus.ImportedCount = statusResult.ImportedCount;
                // Переведем статус в наш формат
                indexingStatus.Status = TranslateStatus(statusResult.Status);
                // Попытаемся исходя из статуса получить общую ошибку
                if (statusResult.Status == ImportStatus.FinishedWithErrors) indexingStatus.ErrorLog = "Unknown error";
                if (statusResult.Status == ImportStatus.Stopped) indexingStatus.ErrorLog = "Indexing has interrupted";
                if (statusResult.Status == ImportStatus.Timeout) indexingStatus.ErrorLog = "Indexing timout";
                // Получим детализацию индексации
                indexingStatus.DetailList = new List<ImportRecordDetails>();
                foreach (var r in statusResult.ImportRecordDetails)
                {
                    indexingStatus.DetailList.Add(new ImportRecordDetails { KEY_DataSource = r.KEY_DataSource, ID_DataSource = r.ID_DataSource, DateStart = r.DateStart, DateEnd = r.DateEnd, ImportedIndicatorsCount = r.ImportedIndicatorsCount, TotalIndicatorsCount = r.TotalIndicatorsCount, ErrorLog = r.ErrorLog, Status = TranslateStatus(r.Status) });
                }
            }
            catch (Exception e)
            {
                logHandler.WriteLogStr("GetQueueStatus: " + e.Message);
            }
            //
            return indexingStatus;
        }

        // Функция преобразует статус индексатора к статусу библиотеки (QueueStatuses)
        public static QueueStatuses TranslateStatus(ImportStatus in_status)
        {
            QueueStatuses status = QueueStatuses.Active;
            if (in_status == ImportStatus.InProcess) status = QueueStatuses.Processing;
            if (in_status == ImportStatus.Finished) status = QueueStatuses.Processed;
            if (in_status == ImportStatus.FinishedWithErrors) status = QueueStatuses.Error;
            if (in_status == ImportStatus.Stopped) status = QueueStatuses.Error;
            if (in_status == ImportStatus.Timeout) status = QueueStatuses.Error;
            return status;
        }
    }

    // Класс описывающий состояние индексации
    public class IndexingStatus
    {
        public QueueStatuses Status;
        public int ImportedCount;
        public int TotalCount;
        public string ErrorLog;
        public List<ImportRecordDetails> DetailList;
    }
    
    // Класс, описывающий детализацию по индексации
    public class ImportRecordDetails
    {
        public int KEY_DataSource;
        public string ID_DataSource;
        public DateTime DateStart;
        public DateTime DateEnd;
        public int ImportedIndicatorsCount;
        public int TotalIndicatorsCount;
        public QueueStatuses Status;
        public string ErrorLog;
    }
    
    // Класс, описывающий структуру очереди
    public class QueueStructure
    {
        public object QUEUE_ID;
        public object KEY;
        public object OBJECT_ID;
        public object INSERT_DATE;
        public object START_INDEXING_DATE;
        public object END_INDEXING_DATE;
        public object STATUS_ID;
        public object LOAD_ERROR_INFO;
        public object PORTALKEY;
    }

    public enum QueueStatuses
    {
        Active = 1,
        Processing = 2,
        Processed = 3,
        Error = 4
    }
}
